import base64
import binascii
import datetime
import hashlib
import re

import rsa
import xlwt

from django.conf import settings
from django.forms import Widget
from django.http import FileResponse
from rest_framework.authentication import SessionAuthentication

from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from wiki.editors.base import BaseEditor
from wiki.editors.markitup import MarkItUpAdminWidget


def file_iterator(filename, chuck_size=512):
    """
    将文件分块返回
    :param filename: 文件名称
    :param chuck_size: 块的大小，默认 512
    :return: 文件以可迭代对象的方式分块返回
    """
    with open(filename, "rb") as f:
        while True:
            c = f.read(chuck_size)
            if c:
                yield c
            else:
                break


def convert_choices_to_dict(choices_list, rv=False):
    choices_dict = dict()
    for c in choices_list:
        if rv:
            choices_dict[c[1]] = c[0]
        else:
            choices_dict[c[0]] = c[1]
    return choices_dict


def create_data_sheet(choices_fields_value, headers):
    wb = xlwt.Workbook()
    sheet = wb.add_sheet("data")
    choice_fields_dict = dict()
    if choices_fields_value:
        for field, choices in choices_fields_value.items():
            choice_fields_dict[field] = convert_choices_to_dict(choices)
    for i, h in enumerate(headers):
        sheet.write(0, i, h)
    return sheet, wb, choice_fields_dict


def sheet_add_row_value(sheet, columns, query, choice_fields_dict, cols):
    for i, k in enumerate(columns):
        v = query.get(k)
        if isinstance(v, datetime.datetime):
            v = v.strftime('%Y-%m-%d %H:%M:%S')
        if isinstance(v, datetime.date):
            v = v.strftime('%Y-%m-%d')
        if k in choice_fields_dict:
            v_display = choice_fields_dict.get(k).get(v)
            if v_display:
                v = v_display
        sheet.write(cols, i, v)
    return sheet


def queryset_export_excel(queryset, headers, columns, filename='file_name', choices_fields_value=None):
    sheet, wb, choice_fields_dict = create_data_sheet(choices_fields_value, headers)
    cols = 1
    for query in queryset.values(*columns):
        sheet = sheet_add_row_value(sheet, columns, query, choice_fields_dict, cols)
        cols += 1
    wb.save(filename)
    response = FileResponse(file_iterator(filename))
    response['Content-Type'] = 'application/vnd.ms-excel'
    response['Content-Disposition'] = 'attachment; filename={0}'.format(filename)
    return response


def list_export_excel(data_list, headers, columns, filename='file_name', choices_fields_value=None):
    sheet, wb, choice_fields_dict = create_data_sheet(choices_fields_value, headers)
    cols = 1
    for row in data_list:
        sheet = sheet_add_row_value(sheet, columns, row, choice_fields_dict, cols)
        cols += 1
    wb.save(filename)
    response = FileResponse(file_iterator(filename))
    response['Content-Type'] = 'application/vnd.ms-excel'
    response['Content-Disposition'] = 'attachment; filename={0}'.format(filename)
    return response


class CsrfExemptSessionAuthentication(SessionAuthentication):

    def enforce_csrf(self, request):
        return True  # To not perform the csrf check previously happening


class NormalResultsSetPagination(PageNumberPagination):
    page_size = 10
    page_size_query_param = 'page_size'
    max_page_size = 20

    def get_paginated_response(self, data):
        if not self.page.has_next():
            next_page = None
        else:
            next_page = self.page.next_page_number()
        return Response({
            'next': self.get_next_link(),
            'previous': self.get_previous_link(),
            'next_page': next_page,
            'length': len(data),
            'count': self.page.paginator.count,
            'page': self.page.number,
            'num_pages': self.page.paginator.num_pages,
            'page_size': self.page.paginator.per_page,
            'results': data
        })


def generate_keys():
    """
    生成公钥和私钥
    :return:
    """
    public_key, private_key = rsa.newkeys(1024)
    pub = public_key.save_pkcs1()
    public_file = open('public.pem', 'wb')
    public_file.write(pub)
    public_file.close()
    pri = private_key.save_pkcs1()
    private_file = open('private.pem', 'wb')
    private_file.write(pri)
    private_file.close()


def rsa_encrypt(d_str):
    """
    对文本进行加密
    :param d_str: 文本
    :return: 加密后的数据
    """
    p = settings.PUBKEY.encode()
    public_key = rsa.PublicKey.load_pkcs1(p)
    # 将字符串进行编码
    content = d_str.encode('utf-8')
    # 公钥加密
    crypto = rsa.encrypt(content, public_key)
    return base64.b64encode(crypto).decode()


def rsa_decrypt(crypto):
    """
    对文本进行解密
    :param crypto: 密文
    :return: 解密后的数据
    """
    p = settings.PRIVKEY.encode()
    private_key = rsa.PrivateKey.load_pkcs1(p)
    # 解密
    content = rsa.decrypt(base64.b64decode(crypto), private_key)
    # 解码
    content = content.decode('utf-8')
    return content


def pbkdf2_hmac_encrypt(d_str):
    """
    单向加密数据
    :param d_str: 文本
    :return: 加密后的数据
    """
    dk = hashlib.pbkdf2_hmac('sha256', d_str.encode(), settings.SECRET_KEY.encode(), 100)
    crypto = binascii.hexlify(dk).decode()
    return crypto


def str_value(string, upper=False):
    """
    去除文本的空格
    :param string: 文本数据
    :param upper: 是否要大写
    :return: 返回格式化后的数据
    """
    if upper is True:
        res = str(string).upper()
    else:
        res = str(string)
    if res:
        return res.strip()
    else:
        return None


def num_value(num, decimal_places=4, return_zero=False):
    """
    数字数据的处理
    :param num: 数字数据
    :param decimal_places: 小数点的位数
    :param return_zero: 默认返回0
    :return: 返回处理后的数据
    """
    if num is None:
        if return_zero:
            res = 0
        else:
            res = None
    else:
        try:
            res = round(float(num), decimal_places)
        except (ValueError, TypeError):
            if return_zero:
                res = 0
            else:
                res = None
    return res


def date_value(ori_date):
    """
    日期数据的处理
    :param ori_date: 原始日期数据
    :return: 返回处理后的数据
    """
    res = None
    try:
        if isinstance(ori_date, str):
            if re.match(r'\d{4}\.\d{1,2}.\d{1,2}', ori_date):
                res = datetime.datetime.strptime(ori_date, '%Y.%m.%d').strftime('%Y-%m-%d')
            if re.match(r'\d{4}/\d{1,2}/\d{1,2}', ori_date):
                res = datetime.datetime.strptime(ori_date, '%Y/%m/%d').strftime('%Y-%m-%d')
            if re.match(r'\d{4}-\d{1,2}-\d{1,2}', ori_date):
                ori_date = re.findall(r'\d{4}-\d{1,2}-\d{1,2}', ori_date)[0]
                res = datetime.datetime.strptime(ori_date, '%Y-%m-%d').strftime('%Y-%m-%d')
        elif isinstance(ori_date, int):
            res_d = datetime.datetime.strptime('1899-12-30', '%Y-%m-%d') + datetime.timedelta(days=ori_date)
            res = datetime.datetime.strftime(res_d, '%Y-%m-%d')
        elif isinstance(ori_date, (datetime.datetime, datetime.date)):
            res = ori_date.strftime('%Y-%m-%d')
        else:
            print(ori_date)
            res = None
    except ValueError:
        print(ori_date)
        res = None
    return res


def plato2sg(plato=12):
    """
    将糖度转化为比重
    :param plato: 糖度
    :return: 比重
    """
    sg = 1 + (plato/(258.6-((plato/258.2)*227.1)))
    return sg


def sg2plato(sg=1.048):
    """
    将比重转化为糖度
    :param sg: 比重
    :return: 糖度
    """
    plato = (-1 * 616.868) + (1111.14 * sg) - (630.272 * sg ** 2) + (135.997 * sg ** 3)
    return plato


class MarkItUpWidget(Widget):
    template_name = "wiki/forms/markitup.html"

    def __init__(self, attrs=None):
        # The 'rows' and 'cols' attributes are required for HTML correctness.
        default_attrs = {
            "class": "editormd",
            # "rows": "10",
            # "cols": "40",
        }
        if attrs:
            default_attrs.update(attrs)
        super().__init__(default_attrs)


class MarkItUp(BaseEditor):
    editor_id = "markitup"

    def get_admin_widget(self, instance=None):
        return MarkItUpAdminWidget()

    def get_widget(self, instance=None):
        return MarkItUpWidget()

    class AdminMedia:
        css = {
            "all": (
                # "wiki/markitup/skins/simple/style.css",
                # "wiki/markitup/sets/admin/style.css",
                "plugins/editor.md/css/editormd.min.css",
            )
        }
        js = (
            # "wiki/markitup/admin.init.js",
            # "wiki/markitup/jquery.markitup.js",
            # "wiki/markitup/sets/admin/set.js",
            "plugins/editor.md/editormd.min.js",
        )

    class Media:
        css = {
            "all": (
                # "wiki/markitup/skins/simple/style.css",
                # "wiki/markitup/sets/frontend/style.css",
                "plugins/editor.md/css/editormd.min.css",
            )
        }
        js = (
            # "wiki/markitup/frontend.init.js",
            # "wiki/markitup/jquery.markitup.js",
            # "wiki/markitup/sets/frontend/set.js",
            "plugins/editor.md/editormd.min.js",
        )
